package com.mm.util;

import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.CompleteMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadRequest;
import com.aliyun.oss.model.InitiateMultipartUploadResult;
import com.aliyun.oss.model.PartETag;
import com.aliyun.oss.model.UploadPartRequest;
import com.aliyun.oss.model.UploadPartResult;
import com.mm.statics.AliyunOss;

/**
 * 
 * @ClassName: AliyunOssUtil
 * @Description: 阿里云OSS文件上传帮助类
 * @author 杨朝湖
 * @date 2020-11-04 03:05:12
 */
public class AliyunOssUtil {
	/**
	 * 文件分片上传
	 * 
	 * @param request  请求的流
	 * @param FileName 文件夹名称
	 * @return 返回文件路径的集合
	 *
	 */
	public static List<String> OssFileUpload(HttpServletRequest request, String FileName) {
		try {
			// 转换request，解析出request中的文件
			MultipartHttpServletRequest multipartRequest = null;
			if (request instanceof MultipartHttpServletRequest) {
				multipartRequest = (MultipartHttpServletRequest) request;
			}
			List<MultipartFile> files = multipartRequest.getFiles("files");
			List<String> list = new ArrayList<String>();

			// 循环遍历，取出单个文件
			for (int i = 0; i < files.size(); i++) {
				// 获取单个文件
				MultipartFile multipartFile = files.get(i);
				// 文件大小
				long fileLength = multipartFile.getSize();
				String fileName = multipartFile.getOriginalFilename();
				// 创建一个通用的多部分解析器.
				CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(
						request.getSession().getServletContext());
				// 设置编码
				commonsMultipartResolver.setDefaultEncoding("utf-8");
				// 判断 request 是否有文件上传,即多部分请求..
				URL urls = null;
				// 处理文件名称的编码格式
				fileName = new String(fileName.getBytes(), "UTF-8");
				// 重新生成文件名称
				String newFileName = (UUID.randomUUID().toString().replace("-", "") + "-" + fileName);
				// 获取文件重命名之后的文件
				String objectName = FileName + "/" + newFileName;
				// 文件分片上传
				long startTime = System.currentTimeMillis();
				// 创建OSSClient实例。
				OSS ossClient = new OSSClientBuilder().build(AliyunOss.endpoint, AliyunOss.accessKeyId,
						AliyunOss.accessKeySecret);
				/*
				 * 步骤1：初始化一个分片上传事件。
				 */
				InitiateMultipartUploadRequest requests = new InitiateMultipartUploadRequest(AliyunOss.bucketName,
						objectName);
				InitiateMultipartUploadResult result = ossClient.initiateMultipartUpload(requests);
				// 返回uploadId，它是分片上传事件的唯一标识，您可以根据这个ID来发起相关的操作，如取消分片上传、查询分片上传等。
				String uploadId = result.getUploadId();
				/*
				 * 步骤2：上传分片。
				 */
				// partETags是PartETag的集合。PartETag由分片的ETag和分片号组成。
				List<PartETag> partETags = new ArrayList<PartETag>();
				// 计算文件有多少个分片。
				final long partSize = 1 * 1024 * 1024L;

				int partCount = (int) (fileLength / partSize);
				if (fileLength % partSize != 0) {
					partCount++;
				}
				// 遍历分片上传。
				for (int j = 0; j < partCount; j++) {
					long startPos = j * partSize;
					long curPartSize = (j + 1 == partCount) ? (fileLength - startPos) : partSize;
					InputStream inputStream = multipartFile.getInputStream();
					// 跳过已经上传的分片。
					inputStream.skip(startPos);
					UploadPartRequest uploadPartRequest = new UploadPartRequest();
					uploadPartRequest.setBucketName(AliyunOss.bucketName);
					uploadPartRequest.setKey(objectName);
					uploadPartRequest.setUploadId(uploadId);
					uploadPartRequest.setInputStream(inputStream);
					// 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100KB。
					uploadPartRequest.setPartSize(curPartSize);

					// 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
					uploadPartRequest.setPartNumber(j + 1);
					// 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
					UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);

					// 每次上传分片之后，OSS的返回结果会包含一个PartETag。PartETag将被保存到partETags中。
					partETags.add(uploadPartResult.getPartETag());
				}
				/*
				 * 步骤3：完成分片上传。
				 */
				// 排序。partETags必须按分片号升序排列。
				Collections.sort(partETags, new Comparator<PartETag>() {
					public int compare(PartETag p1, PartETag p2) {
						return p1.getPartNumber() - p2.getPartNumber();
					}
				});
				// 在执行该操作时，需要提供所有有效的partETags。OSS收到提交的partETags后，会逐一验证每个分片的有效性。当所有的数据分片验证通过后，OSS将把这些分片组合成一个完整的文件。
				CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(
						AliyunOss.bucketName, objectName, uploadId, partETags);
				ossClient.completeMultipartUpload(completeMultipartUploadRequest);

				// 关闭OSSClient。
				ossClient.shutdown();
				long endTime = System.currentTimeMillis();

				System.err.println("执行时间：" + (endTime - startTime) + " ms");
				// 设置URL过期时间为10年 3600l* 1000*24*365*10
				Date expiration = new Date(new Date().getTime() + 3600l * 1000 * 24 * 365 * 10);
				// url = putResult.getETag();
				urls = ossClient.generatePresignedUrl(AliyunOss.bucketName, objectName, expiration);

				// 获取上传之后的文件名
				String uploadImage = urls.toString().split("\\?")[0];
				list.add(uploadImage);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
}
